package org.abc.fund.service.factorService;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import org.abc.fund.entity.factorEntity.NavData;
import org.springframework.stereotype.Service;

import java.io.BufferedReader;
import java.io.InputStreamReader;
import java.nio.charset.StandardCharsets;
import java.time.temporal.ChronoUnit;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.concurrent.TimeUnit;
import java.util.stream.Collectors;

@Service
public class FactorAlgorithm {
    private static final String PY = "python";
//相对路径，别改
    private static final String SCRIPT = "C:\\Users\\19213\\IdeaProjects\\TouGu\\fetch_rf_daily.py";
    String token = "161d70718f4938627e11a3b55a63c63eae7e5ecd489b436f9c4b2e44";
    public double fetchRfDaily(String start, String end) {
        try {
            Process p = new ProcessBuilder(PY, SCRIPT, token, start, end)
                    .redirectErrorStream(true)
                    .start();
            String json = new BufferedReader(
                    new InputStreamReader(p.getInputStream(), StandardCharsets.UTF_8))
                    .lines().collect(Collectors.joining());
            p.waitFor();
            ObjectMapper om = new ObjectMapper();
            List<Map<String,Object>> list = om.readValue(
                    json,
                    new TypeReference<List<Map<String,Object>>>() {}
            );
            if (list.isEmpty()) {
                throw new RuntimeException("无风险率数据");
            }
            Map<String,Object> last = list.get(list.size() - 1);
            Object y = last.get("yield");
            return (y instanceof Number)
                    ? ((Number) y).doubleValue()
                    : Double.parseDouble(y.toString());

        } catch (Exception ex) {
            throw new RuntimeException("拉取无风险率失败", ex);
        }
    }

    public double calculateSimpleReturn(List<NavData> navDataList) {
        if (navDataList.size() < 2) return 0;
        double startNav = navDataList.get(0).getUnitNav();
        double endNav = navDataList.get(navDataList.size()-1).getUnitNav();
        return (endNav - startNav) / startNav;
    }
    public double calculateLogReturn(List<NavData> navDataList) {
        if (navDataList.size() < 2) return 0;
        double startNav = navDataList.get(0).getUnitNav();
        double endNav = navDataList.get(navDataList.size()-1).getUnitNav();
        return Math.log(endNav / startNav);
    }
    public double calculateAnnualizedReturn(List<NavData> navDataList) {
        if (navDataList.size() < 2) return 0;

        double totalReturn = calculateSimpleReturn(navDataList);
        long daysBetween = ChronoUnit.DAYS.between(
                navDataList.get(0).getDate(),
                navDataList.get(navDataList.size()-1).getDate()
        );

        return Math.pow(1 + totalReturn, 365.0 / daysBetween) - 1;
    }

    public double calculateVolatility(List<NavData> navDataList) {
        if (navDataList.size() < 2) return 0;

        List<Double> returns = new ArrayList<>();
        for (int i = 1; i < navDataList.size(); i++) {
            double ret = navDataList.get(i).getDailyGrowthRate();
            returns.add(ret);
        }

        double mean = returns.stream().mapToDouble(Double::doubleValue).average().orElse(0);
        double variance = returns.stream()
                .mapToDouble(r -> Math.pow(r - mean, 2))
                .average().orElse(0);

        return Math.sqrt(variance);
    }
    public double calculateAnnualizedVolatility(List<NavData> navDataList) {
        double dailyVolatility = calculateVolatility(navDataList);
        return dailyVolatility * Math.sqrt(252); // 252个交易日
    }
    public double calculateMaxDrawdown(List<NavData> navDataList) {
        double maxDrawdown = 0;
        double peak = navDataList.get(0).getUnitNav();

        for (NavData data : navDataList) {
            double nav = data.getUnitNav();
            if (nav > peak) peak = nav;

            double drawdown = (peak - nav) / peak;
            if (drawdown > maxDrawdown) {
                maxDrawdown = drawdown;
            }
        }

        return maxDrawdown;
    }
    public double calculateDownsideRisk(List<NavData> navDataList, double minAcceptableReturn) {
        if (navDataList.size() < 2) return 0;

        List<Double> negativeReturns = new ArrayList<>();
        for (int i = 1; i < navDataList.size(); i++) {
            double ret = navDataList.get(i).getDailyGrowthRate();
            if (ret < minAcceptableReturn) {
                negativeReturns.add(Math.pow(ret - minAcceptableReturn, 2));
            }
        }

        if (negativeReturns.isEmpty()) return 0;

        double sum = negativeReturns.stream().mapToDouble(Double::doubleValue).sum();
        return Math.sqrt(sum / (negativeReturns.size() - 1));
    }
    public double calculateSharpeRatio(List<NavData> navDataList, double riskFreeRate) {
        double annualizedReturn = calculateAnnualizedReturn(navDataList);
        double annualizedVol = calculateAnnualizedVolatility(navDataList);
        if (annualizedVol == 0) return 0;

        return (annualizedReturn - riskFreeRate) / annualizedVol;
    }
    public double calculateMovingAverage(List<NavData> navDataList, long diffInMillies) {
        long diff = TimeUnit.DAYS.convert(diffInMillies, TimeUnit.MILLISECONDS);
        // 使用天数差作为 window 参数
        int window = (int) diff;
        if (navDataList.size() < window) return 0;
        return navDataList.stream()
                .skip(navDataList.size() - window)
                .mapToDouble(NavData::getUnitNav)
                .average()
                .orElse(0);
    }
    public double calculateRSI(List<NavData> navDataList) {
        int period = 14;
        if (navDataList.size() <= period) return 50; // 中性值

        double avgGain = 0;
        double avgLoss = 0;

        // 计算初始平均值
        for (int i = 1; i <= period; i++) {
            double change = navDataList.get(i).getDailyGrowthRate();
            if (change > 0) avgGain += change;
            else avgLoss += Math.abs(change);
        }

        avgGain /= period;
        avgLoss /= period;

        // 计算后续值
        for (int i = period + 1; i < navDataList.size(); i++) {
            double change = navDataList.get(i).getDailyGrowthRate();
            double currentGain = change > 0 ? change : 0;
            double currentLoss = change < 0 ? Math.abs(change) : 0;

            avgGain = (avgGain * (period - 1) + currentGain) / period;
            avgLoss = (avgLoss * (period - 1) + currentLoss) / period;
        }

        if (avgLoss == 0) return 100;
        double rs = avgGain / avgLoss;
        return 100 - (100 / (1 + rs));
    }
    public List<Double> calculateRedemptionStatusFactor(List<NavData> navDataList) {
        Map<String, Double> factors = new HashMap<>();
        List<Double> redemptions = new ArrayList<>();
        // 当前状态
        NavData latest = navDataList.get(navDataList.size()-1);
        // 历史开放频率
        long totalDays = navDataList.size();
        long purchaseOpenDays = navDataList.stream()
                .filter(data -> "开放申购".equals(data.getPurchaseStatus()))
                .count();
        long redemptionOpenDays = navDataList.stream()
                .filter(data -> "开放赎回".equals(data.getRedemptionStatus()))
                .count();

        redemptions.add((double)purchaseOpenDays / totalDays);
        redemptions.add((double)redemptionOpenDays / totalDays);
        return redemptions;
    }


}
